/*
 * @ (#) CmppSubmitPacket.java Jun 8, 2008
 *
 * 
 */
package com.aspire.nm.component.cmppserver.filter.coder.packet;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;

import com.aspire.nm.component.commonUtil.bytes.BytesUtil;



/**
 * CMPP_SUBMIT
 * 
 * <p>
 * 参照：中国移动通信互联网短信网关接口协议CMPP3.0, 中国移动通信互联网短信网关接口协议CMPP2.0
 * </p>
 * 
 * @author Wang Shenggong
 * @version 1.0.0
 * @since 1.0.0
 * 
 */
public class CmppSubmitPacket extends CmppPacket{
	private static final long serialVersionUID = 1L;
	public static final int REGISTERED_DILIVERY_YES = 1;
	public static final int REGISTERED_DELIVERY_NO = 0;
	public static final int FEE_USR_TYPE_DEST_MSISDN = 0;
	public static final int FEE_USR_TYPE_SRC_MSISDN = 1;
	public static final int FEE_USR_TYPE_SP = 2;
	public static final int FEE_USR_TYPE_TERMINAL = 3;
	public static final int FEE_TERMINAL_TYPE_REAL = 0;
	public static final int FEE_TERMINAL_TYPE_MOCK = 1;
	public static final int MSG_FMT_ASCII = 0;
	public static final int MSG_FMT_WRITE_CARD = 3;
	public static final int MSG_FMT_BINARY = 4;
	public static final int MSG_FMT_UCS2 = 8;
	public static final int MSG_FMT_GBK = 15;
	public static final int MSG_FMT_FLASH = 24;
	public static final String FEE_TYPE_FREE = "01";

	// 信息标识，由SP接入的短信网关本身产生，本处填空。
	private long msgId;

	// 相同Msg_Id的信息总条数，从1开始
	private int pkTotal;

	// 相同Msg_Id的信息序号，从1开始
	private int pkNumber;

	// 是否要求返回状态确认报告： 0：不需要 1：需要
	private byte registeredDelivery;

	// 信息级别
	private byte msgLevel;

	// 业务标识，是数字、字母和符号的组合。
	private String serviceId;

	// 计费用户类型字段 0：对目的终端MSISDN计费； 1：对源终端MSISDN计费； 2：对SP计费; 3：表示本字段无效，对谁计费参见Fee_terminal_Id字段。
	private byte feeUserType;

	// 被计费用户的号码
	private String feeTerminalId;

	// 被计费用户的号码类型，0：真实号码；1：伪码
	private byte feeTerminalType;

	// GSM协议类型
	private byte tpPid;

	// GSM协议类型
	private byte tpUdhi;

	// 信息格式
	private byte msgFmt;

	// 信息内容来源(SP_Id)
	private String msgSrc;

	// 资费类别
	private String feeType;

	// 资费代码
	private String feeCode;

	// 存活有效期，格式遵循SMPP3.3协议
	private String validTime;

	// 定时发送时间，格式遵循SMPP3.3协议
	private String atTime;

	// 源号码
	private String srcId;

	// 接收信息的用户数量(小于100个用户)
	private byte destUsrTl;

	// 接收短信的MSISDN号码 32*DestUsr_tl
	private String[] destTerminalId;

	// 接收短信的用户的号码类型，0：真实号码；1：伪码
	private byte destTerminalType;

	// 信息长度(Msg_Fmt值为0时：<160个字节；其它<=140个字节)
	private int msgLength;

	// 信息内容
	private byte[] msgContent;

	// 点播业务使用的LinkID，非点播类业务的MT流程不使用该字段
	private String linkId;

	public CmppSubmitPacket(Version version) {
		super(version);
		commandId = CmppPacket.CMPP_SUBMIT;
	}


	@Override
	public byte[] pack() throws PacketException {

		int mainLength = version.isVersion3() ? 151 : 126;
		msgLength = msgContent.length;
		int destTerminalIdLength = destTerminalId.length * (version.isVersion3() ? 32 : 21);

		totalLength = 12 + mainLength + msgLength + destTerminalIdLength;
		super.pack();

		try {
			putLong(msgId);
			put((byte) pkTotal);
			put((byte) pkNumber);
			put(registeredDelivery);
			put(msgLevel);
			putStringRightPad(serviceId, 10);
			put(feeUserType);
			putStringRightPad(feeTerminalId, version.isVersion3() ? 32 : 21);
			if (version.isVersion3()) {
				put(feeTerminalType);
			}
			put(tpPid);
			put(tpUdhi);
			put(msgFmt);
			putStringRightPad(msgSrc, 6);
			putStringRightPad(feeType, 2);
			putStringRightPad(feeCode, 6);
			putStringRightPad(validTime, 17);
			putStringRightPad(atTime, 17);
			putStringRightPad(srcId, 21);
			put(destUsrTl);
			for (int i = 0; i < destUsrTl; i++) {
				putStringRightPad(destTerminalId[i], version.isVersion3() ? 32 : 21);
			}

			if (version.isVersion3()) {
				put(destTerminalType);
			}
			put((byte) msgLength);
			put(msgContent);
			putStringRightPad(linkId, version.isVersion3() ? 20 : 8);
		} catch (Exception e) {
			throw new PacketException("Unexpected Exception on packing CmppSubmitPacket: TotalLength=" + totalLength
					+ ", CommandId=" + Integer.toHexString(commandId) + ", SequenceId=" + sequenceId + ", Version="
					+ version + ",byteBufferSize=" + this.getByteBuffer().capacity() + ",byteBufferPosion="
					+ this.getByteBuffer().position() + ",byteBufferRemain=" + this.getByteBuffer().remaining()
					+ ",linkId=" + linkId, e);
		}

		return getBytes();
	}

	@Override
	public CmppPacket unpack(ByteBuffer buffer) throws PacketException {

		super.unpack(buffer);

		try {
			msgId = getLong();
			pkTotal = get();
			pkNumber = get();
			registeredDelivery = get();
			msgLevel = get();
			serviceId = getString(10);
			feeUserType = get();
			feeTerminalId = getString(version.isVersion3() ? 32 : 21);
			if (version.isVersion3()) {
				feeTerminalType = get();
			}
			tpPid = get();
			tpUdhi = get();
			msgFmt = get();
			msgSrc = getString(6);
			feeType = getString(2);
			feeCode = getString(6);
			validTime = getString(17);
			atTime = getString(17);
			srcId = getString(21);
			destUsrTl = get();

			destTerminalId = new String[destUsrTl];
			for (int i = 0; i < destUsrTl; i++) {
				destTerminalId[i] = getString(version.isVersion3() ? 32 : 21);
			}
			if (version.isVersion3()) {
				destTerminalType = get();
			}

			msgLength = get();
			if (msgLength < 0) {
				msgLength += 256;
			}
			msgContent = new byte[msgLength];
			get(msgContent);
			linkId = getString(version.isVersion3() ? 20 : 8);

		} catch (Exception e) {
			throw new PacketException("Unexpected Exception on unpacking CmppSubmitPacket:Version=" + version
					+ " TotalLength=" + totalLength + ", CommandId=" + Integer.toHexString(commandId) + ", SequenceId="
					+ sequenceId, e);
		}

		return this;
	}

	// -- public - get & set --------------------------------------------------

	/**
	 * @return the msgId
	 */
	public long getMsgId() {
		return msgId;
	}

	/**
	 * @param msgId the msgId to set
	 */
	public void setMsgId(long msgId) {
		this.msgId = msgId;
	}

	/**
	 * @return the pkTotal
	 */
	public int getPkTotal() {
		return pkTotal;
	}

	/**
	 * @param pkTotal the pkTotal to set
	 */
	public void setPkTotal(int pkTotal) {
		this.pkTotal = pkTotal;
	}

	/**
	 * @return the pkNumber
	 */
	public int getPkNumber() {
		return pkNumber;
	}

	/**
	 * @param pkNumber the pkNumber to set
	 */
	public void setPkNumber(int pkNumber) {
		this.pkNumber = pkNumber;
	}

	/**
	 * @return the registeredDelivery
	 */
	public byte getRegisteredDelivery() {
		return registeredDelivery;
	}

	/**
	 * @param registeredDelivery the registeredDelivery to set
	 */
	public void setRegisteredDelivery(byte registeredDelivery) {
		this.registeredDelivery = registeredDelivery;
	}

	/**
	 * @return the msgLevel
	 */
	public byte getMsgLevel() {
		return msgLevel;
	}

	/**
	 * @param msgLevel the msgLevel to set
	 */
	public void setMsgLevel(byte msgLevel) {
		this.msgLevel = msgLevel;
	}

	/**
	 * @return the serviceId
	 */
	public String getServiceId() {
		return serviceId;
	}

	/**
	 * @param serviceId the serviceId to set
	 */
	public void setServiceId(String serviceId) {
		this.serviceId = serviceId;
	}

	/**
	 * @return the feeUserType
	 */
	public byte getFeeUserType() {
		return feeUserType;
	}

	/**
	 * @param feeUserType the feeUserType to set
	 */
	public void setFeeUserType(byte feeUserType) {
		this.feeUserType = feeUserType;
	}

	/**
	 * @return the feeTerminalId
	 */
	public String getFeeTerminalId() {
		return feeTerminalId;
	}

	/**
	 * @param feeTerminalId the feeTerminalId to set
	 */
	public void setFeeTerminalId(String feeTerminalId) {
		this.feeTerminalId = feeTerminalId;
	}

	/**
	 * @return the feeTerminalType
	 */
	public byte getFeeTerminalType() {
		return feeTerminalType;
	}

	/**
	 * @param feeTerminalType the feeTerminalType to set
	 */
	public void setFeeTerminalType(byte feeTerminalType) {
		this.feeTerminalType = feeTerminalType;
	}

	/**
	 * @return the tpPid
	 */
	public byte getTpPid() {
		return tpPid;
	}

	/**
	 * @param tpPid the tpPid to set
	 */
	public void setTpPid(byte tpPid) {
		this.tpPid = tpPid;
	}

	/**
	 * @return the tpUdhi
	 */
	public byte getTpUdhi() {
		return tpUdhi;
	}

	/**
	 * @param tpUdhi the tpUdhi to set
	 */
	public void setTpUdhi(byte tpUdhi) {
		this.tpUdhi = tpUdhi;
	}

	/**
	 * @return the msgFmt
	 */
	public byte getMsgFmt() {
		return msgFmt;
	}

	/**
	 * @param msgFmt the msgFmt to set
	 */
	public void setMsgFmt(byte msgFmt) {
		this.msgFmt = msgFmt;
	}

	/**
	 * @return the msgSrc
	 */
	public String getMsgSrc() {
		return msgSrc;
	}

	/**
	 * @param msgSrc the msgSrc to set
	 */
	public void setMsgSrc(String msgSrc) {
		this.msgSrc = msgSrc;
	}

	/**
	 * @return the feeType
	 */
	public String getFeeType() {
		return feeType;
	}

	/**
	 * @param feeType the feeType to set
	 */
	public void setFeeType(String feeType) {
		this.feeType = feeType;
	}

	/**
	 * @return the feeCode
	 */
	public String getFeeCode() {
		return feeCode;
	}

	/**
	 * @param feeCode the feeCode to set
	 */
	public void setFeeCode(String feeCode) {
		this.feeCode = feeCode;
	}

	/**
	 * @return the validTime
	 */
	public String getValidTime() {
		return validTime;
	}

	/**
	 * @param validTime the validTime to set
	 */
	public void setValidTime(String validTime) {
		this.validTime = validTime;
	}

	/**
	 * @return the atTime
	 */
	public String getAtTime() {
		return atTime;
	}

	/**
	 * @param atTime the atTime to set
	 */
	public void setAtTime(String atTime) {
		this.atTime = atTime;
	}

	/**
	 * @return the srcId
	 */
	public String getSrcId() {
		return srcId;
	}

	/**
	 * @param srcId the srcId to set
	 */
	public void setSrcId(String srcId) {
		this.srcId = srcId;
	}

	/**
	 * @return the destUsrTl
	 */
	public byte getDestUsrTl() {
		return destUsrTl;
	}

	/**
	 * @param destUserTl the destUsrTl to set
	 */
	public void setDestUsrTl(byte destUserTl) {
		this.destUsrTl = destUserTl;
	}

	/**
	 * @return the destTerminalId
	 */
	public String[] getDestTerminalId() {
		return destTerminalId;
	}

	/**
	 * @param destTerminalId the destTerminalId to set
	 */
	public void setDestTerminalId(String[] destTerminalId) {
		this.destTerminalId = destTerminalId;
	}

	/**
	 * 返回 Dest_Terminate_Id的字符串
	 * 
	 * @return
	 */
	public String getDestTerminateIds() {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < destTerminalId.length; i++) {
			sb.append(destTerminalId[i]).append(i == (destTerminalId.length - 1) ? "" : ",");
		}
		return sb.toString();
	}
	
	/**
	 * @return the destTerminalType
	 */
	public byte getDestTerminalType() {
		return destTerminalType;
	}

	/**
	 * @param destTerminalType the destTerminalType to set
	 */
	public void setDestTerminalType(byte destTerminalType) {
		this.destTerminalType = destTerminalType;
	}

	/**
	 * @return the msgLength
	 */
	public int getMsgLength() {
		return msgLength;
	}

	/**
	 * @param msgLength the msgLength to set
	 */
	public void setMsgLength(int msgLength) {
		this.msgLength = msgLength;
	}

	/**
	 * @return the msgContent
	 */
	public byte[] getMsgContent() {
		return msgContent;
	}

	/**
	 * 获取长短信唯一标示
	 * @return
	 */
	private String getLongSmsPkMsg(){
		if(msgContent[0]==0x05){//6位头
			byte[] ret=new byte[2];
			ret[0]=0x00;
			ret[1]=msgContent[3];
			return BytesUtil.bytes2hex(ret);
		}else if(msgContent[0]==0x06){//7位头
			byte[] ret=new byte[2];
			System.arraycopy(msgContent, 3, ret, 0, 2);
			return BytesUtil.bytes2hex(ret);
		}
		return null;
	}
	/**
	 * 拼接长短信时候的Key
	 * @return
	 */
	public String getLongPartKey(){
		String longSmsPkMsg = getLongSmsPkMsg();
		if(longSmsPkMsg == null) return "";
		else return getDestTerminateIds() + "_" + longSmsPkMsg + "_" + getPkNumber() + "_" + getPkTotal();
	}
	
	
	/**
	 * 返回根据Msg_Fmt解码的字符串消息
	 * 
	 * @return
	 */
	public String getMsgContentDecoded() {
		String ret = null;
		try {
			switch (msgFmt) {
			case 0:
				ret = new String(msgContent, "US-ASCII");
				break;
			case 4:
				ret = BytesUtil.bytes2hex(msgContent);
				break;
			case 8:
			case 24:
				 byte longSmsFirstByte = this.msgContent[0];
				 byte[] contentBytes = new byte[this.msgContent.length - (longSmsFirstByte + 1)];
				 System.arraycopy(this.msgContent, longSmsFirstByte + 1, contentBytes, 0, contentBytes.length);
				 byte[] tmpHeader = new byte[longSmsFirstByte + 1];
				 System.arraycopy(this.msgContent, 0, tmpHeader, 0, tmpHeader.length);
				 ret = new String(contentBytes, "UTF-16BE");
				break;
			case 15:
				ret = new String(msgContent, "GBK");
				break;
			default:
				ret = new String(msgContent);
			}
		} catch (UnsupportedEncodingException e) {
			ret = new String(msgContent);
		}

		return ret;
	}

	/**
	 * @param msgContent the msgContent to set
	 */
	public void setMsgContent(byte[] msgContent) {
		this.msgContent = msgContent;
	}

	/**
	 * @return the linkId
	 */
	public String getLinkId() {
		return linkId;
	}

	public String getReserve() {
		return linkId;
	}

	/**
	 * @param linkId the linkId to set
	 */
	public void setLinkId(String linkId) {
		this.linkId = linkId;
	}

	public void setReserve(String reserve) {
		this.linkId = reserve;
	}

	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();

		sb.append("CMPP_SUBMIT:");
		sb.append("Sequence_Id=").append(sequenceId);
		sb.append(", Msg_Id=").append(msgId);
		sb.append(", Pk_total=").append(pkTotal);
		sb.append(", Pk_number=").append(pkNumber);
		sb.append(", Registered_Delivery=").append(registeredDelivery);
		sb.append(", Msg_level=").append(msgLevel);
		sb.append(", Service_Id=").append(serviceId);
		sb.append(", Fee_UserType=").append(feeUserType);
		sb.append(", Fee_terminal_Id=").append(feeTerminalId);
		sb.append(", Fee_terminal_type=").append(feeTerminalType);
		sb.append(", TP_pId=").append(tpPid);
		sb.append(", TP_udhi=").append(tpUdhi);
		sb.append(", Msg_Fmt=").append(msgFmt);
		sb.append(", Msg_src=").append(msgSrc);
		sb.append(", FeeType=").append(feeType);
		sb.append(", FeeCode=").append(feeCode);
		sb.append(", ValId_Time=").append(validTime);
		sb.append(", At_Time=").append(atTime);
		sb.append(", Src_Id=").append(srcId);
		sb.append(", DestUsr_tl=").append(destUsrTl);
		sb.append(", Dest_terminal_Id=").append(getDestTerminateIds());
		sb.append(", Dest_terminal_type=").append(destTerminalType);
		sb.append(", Msg_Length=").append(msgLength);
		sb.append(", Msg_Content=").append(getMsgContentDecoded());
		sb.append(", LinkID=").append(linkId);
		sb.append(", LongPartKey=").append(getLongPartKey());
		
		return sb.toString();
	}
}
